package com.seek.service.imp;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONObject;
import com.lianlianpay.constant.PaymentStatusEnum;
import com.seek.cache.CacheUtil;
import com.seek.common.handler.SpringContextHolder;
import com.seek.core.BaseServiceImpl;
import com.seek.dao.LoanDao;
import com.seek.inter.interdto.InterDtoCUser;
import com.seek.model.dto.ResultInfo;
import com.seek.model.entity.TLoan;
import com.seek.model.entity.TLoanRepayment;
import com.seek.service.BizFlowService;
import com.seek.service.CompanyAgentMoneyService;
import com.seek.service.LoanService;
import com.seek.service.ReadService;
import com.seek.util.CommonUtil;
import com.seek.util.ConstantsUtil;
import com.seek.util.DateUtil;
import com.seek.util.JsonUtil;
import com.seek.util.Push2SingleUtil;

@Repository("loanService")
public class LoanServiceImpl extends BaseServiceImpl<TLoan> implements LoanService {

	private static Logger logger = LoggerFactory.getLogger(LoanServiceImpl.class);

	private CompanyAgentMoneyService companyAgentMoneyService = (CompanyAgentMoneyService) SpringContextHolder.getBean("companyAgentMoneyService");
	private ReadService readService = (ReadService) SpringContextHolder.getBean("readService");
	private Push2SingleUtil push2SingleUtil = (Push2SingleUtil)SpringContextHolder.getBean("push2SingleUtil");
	private BizFlowService bizFlowService = (BizFlowService)SpringContextHolder.getBean("bizFlowService");
	@Autowired
	public void setDao(LoanDao loanDao) {
		super.setDao(loanDao);
	}
	public LoanDao getDao() {
		return (LoanDao) super.getDao();
	}

	/**
	 * todo:自动审核成功后的放款
	 */
	@Transactional
	public ResultInfo addLoan(JSONObject reqJson) throws Exception{
		ResultInfo result = new ResultInfo();
		Long companyId = reqJson.getLong("companyId");
		Long custId = reqJson.getLong("custId");
		int reqMoney = reqJson.getIntValue("reqMoney");
		int handleFee = reqJson.getIntValue("handleFee");
		String clientid = reqJson.getString("clientid");
		String signImage = reqJson.getString("signImage");
		String token = reqJson.getString("token");

		InterDtoCUser user = CacheUtil.getCLoginCache(token);
		if (CommonUtil.getIntValue(user.getIsBlack()) == 1){
			result.setStatus(1);
			result.setMessage("抱歉，您当前已限制从本平台中贷款！");
			return result;
		}
		if (user == null || companyId == null || companyId < 1 || custId == null || custId<1 || reqMoney < 10000 || CommonUtil.isEmpty(clientid) || CommonUtil.isEmpty(signImage)){
			result.setStatus(1);
			result.setMessage("参数不完整，请检查！");
			return result;
		}
		String applyDetail = CacheUtil.getRedisData("APPLY_" + user.getCustId());
		if(StringUtils.isNotEmpty(applyDetail)){
			result.setStatus(1);
			result.setMessage("操作过快，请3分钟后再试");
			return result;
		}

		CacheUtil.putRedisDataEx("APPLY_" + user.getCustId(),String.valueOf(reqMoney),3*60L);
		
		// 判断用户当前是否存在逾期未还款订单
		String sql = "select count(1) cnt from t_loan where customer_id=? and loan_status in (3,6) and end_time<now()";
		Map<String,Object> his = readService.findOneMapBySql(sql, new Object[]{user.getCustId()});
		if (his != null && CommonUtil.getIntValue(his.get("cnt"))>0){
			result.setStatus(1);
			result.setMessage("您当前还有订单逾期未还款，请先完成还款！");
			return result;
		}

		// 计算手续费是否正确
		int handleFee1 = getHandleFee(companyId, reqMoney);
		if (Math.abs(handleFee1 - handleFee)>1){
			result.setStatus(1);
			result.setMessage("手续费计算错误，请检查！");
			return result;
		}
		if (CommonUtil.getIntValue(user.getAuthedCredit()) - CommonUtil.getIntValue(user.getUsedCredit()) < reqMoney){
			result.setStatus(1);
			result.setMessage("您的可用余额不足，不能申请！");
			return result;
		}

		// 因为系统设定为一个客户只能在一个设备上登录，所以这里不再强行去校验客户借款额度的问题：即只要申请就允许提交
		TLoan en = new TLoan();
		en.setCompanyId(companyId);
		StringBuffer sb = new StringBuffer("");
		String orderDate = DateUtil.getCurrDate("yyyyMMddHHmmssSSS");
		sb.append(orderDate).append(user.getPhoneNo());
		sb.append(new Random().nextInt(9000) + 1000);
		en.setOrderNo(sb.toString());
		en.setCustomerId(custId);
		en.setLoanType(ConstantsUtil.LOANTYPE_LOAN);
		en.setReqDate(new Date());
		en.setReqMoney(reqMoney);
		en.setHandleFee(handleFee);
		en.setDownMoney(en.getReqMoney() - en.getHandleFee());
		if (ConstantsUtil.DEBUG_MODE==1){
			en.setLoanStatus(3);
			en.setAuditTime(new Date());
			en.setDownTime(new Date());
			en.setAuditorId(-1L);
			en.setAuditRemark("自动审核通过");
		}else{
			if (canAutoAudit(custId)){
				en.setLoanStatus(ConstantsUtil.LOANSTATUS_AUDITED);
				en.setAuditTime(new Date());
				en.setAuditorId(-1L);
				en.setAuditRemark("自动审核通过");
			}else
				en.setLoanStatus(ConstantsUtil.LOANSTATUS_NEW);
		}

		en.setStartTime(DateUtil.getNowDate());
		en.setEndTime(DateUtil.dateFormat(DateUtil.dateFormat(DateUtil.dateAdd(DateUtil.DATE_INTERVAL_DAY, new Date(), 6),DateUtil.DATE_FORMAT_YMD)+" 23:59:59",DateUtil.DATE_FORMAT_YMDHMS));
		en.setClientId(user.getClientid());
		en.setSignImg(signImage);
		en.setAgentId(CommonUtil.getLongValue(user.getAgentId()));
		en.setSubAgentId(CommonUtil.getLongValue(user.getSubAgentId()));
		en = this.getDao().insert(en);

		// 更新客户当前已使用额度
		Map<String,Object> m = new HashMap<String,Object>();
	    m.put("usedCredit", en.getReqMoney());
	    CacheUtil.refreshCustRedis(reqJson.getString("token"), m, 1);
	    // 添加通知
	    if (ConstantsUtil.LOANSTATUS_AUDITED == en.getLoanStatus() && !CommonUtil.isEmpty(user.getClientid())){
	    	push2SingleUtil.push2Single(Push2SingleUtil.TYPE_OF_LOANAUDIT_SUC, user.getClientid());
	    }

		result.setStatus(0);
		result.setMessage("成功！");
		return result;
	}

	// 计算手续费
	@SuppressWarnings("unchecked")
	public int getHandleFee(Long companyId, int reqMoney)  throws Exception{
		// 为保证数据正确，费率不从缓存获取，直接从数据库读取
		String sql = "select type, value from t_config where is_used=1 and company_id=? and type in ('INTERST_DAY','HANDLEFEE_DAY') order by type asc";
		List<Map<String,Object>> data = this.getDao().findMapBySql(sql, new Object[]{companyId});
		int interst, handlefee;
		if (data.size() < 2){
			interst = 3;
			handlefee = 285;
		}else{
			handlefee = CommonUtil.getIntValue(data.get(0).get("value"));
			interst = CommonUtil.getIntValue(data.get(1).get("value"));
		}
		return (int)Math.ceil(((double)(reqMoney * (interst + handlefee) * 7)/10000));
	}

	@SuppressWarnings("rawtypes")
	private boolean canAutoAudit(Long custId){
//		int n = ConstantsUtil.LOAN_AUTO_AUDIT_NUM;
//		StringBuffer sb = new StringBuffer("select a.num from ");
//		sb.append(" (select ifnull(sum(case when a.payback_date<a.end_time then 1 else 0 end),0) num from t_loan a where customer_id=? and a.loan_status in (3,5,6) order by id desc limit 0,"+n+") a")
//		  .append(" where a.num>="+n);
//		List d = this.getDao().findMapBySql(sb.toString(), new Object[]{custId});
//		if (d != null && d.size()>0)
//			return true;
		return false;
	}

	@Transactional
	public void assignLoanOverdue(JSONObject reqJson) throws Exception{
		String loanIds = reqJson.getString("loanIds");
		Long overduerId = reqJson.getLong("overduerId");

		// 如果原货款订单存在分配记录，则设置分配记录的结束时间
		String sql = "update t_loan_overdue_assignlog set end_time=now() where loan_id in ("+loanIds+") and end_time is null";
		this.getDao().executeUpdateSql(sql);
		// 插入新的分配记录
		String[] ids = loanIds.split(",");
		for (String s : ids){
			sql = "insert into t_loan_overdue_assignlog(loan_id,overduer_id,begin_time) select ?,?,now()";
			this.getDao().executeUpdateSql(sql, new Object[]{s,overduerId});
		}
		// 更新货款表中的催收人
		sql = "update t_loan set overduer_id=? where id in ("+loanIds+") and loan_status in (3,6)";
		this.getDao().executeUpdateSql(sql, new Object[]{overduerId});

	}

	@Transactional
	public void addLoanOverdueLog(Long loanId, String remark, String nextOverdueTime, String userId) throws Exception{
		String sql = "insert into t_loan_overdue_log(loan_id,customer_id,create_time,creator_id,next_overdue_time,remark) select a.id,a.customer_id,now(),?,?,? from t_loan a where a.id=?";
		this.getDao().executeUpdateSql(sql, new Object[]{userId,nextOverdueTime,remark,loanId});
		sql = "update t_loan set last_overdue_time=now(),next_overdue_time=? where id=?";
		this.getDao().executeUpdateSql(sql, new Object[]{nextOverdueTime,loanId});
	}

	public void addLatefeeReduceLog(Long loanId, String remark, int money, String userId) throws Exception{
		String sql = "insert into t_loan_latefee(loan_id,type,create_time,creator_id,money,remark) select ?,2,now(),?,?,?";
		this.getDao().executeUpdateSql(sql, new Object[]{loanId,userId,money,remark});
		sql = "update t_loan set off_latefee=off_latefee+? where id=?";
		this.getDao().executeUpdateSql(sql, new Object[]{money,loanId});
	}

	public void addLoanHandPay(Long companyId,Long loanId, int money, int latefee,String userId,String payor, String img) throws Exception{
		String sql = "insert into t_loan_repayment(company_id,loan_id,type,pay_type,pay_money,latefee,pay_time,create_time,pay_status,payor,recievor,reci_img) select ?,?,2,3,?,?,now(),now(),1,?,?,?";
		this.getDao().executeUpdateSql(sql, new Object[]{companyId,loanId,money,latefee,payor,userId,img});
	}

	@SuppressWarnings("unchecked")
	public void refreshCachedUsedCredit(Long loanId) throws Exception{
		// 根据loanid取得客户ID和货款金额
		String sql = "select a.customer_id custId,(select ifnull(sum(b.req_money),0) from t_loan b where b.loan_status in (0,2,3,4,6) and b.customer_id=a.customer_id) usedCredit from t_loan a where a.id=?";
		List<Map<String,Object>> datas = this.getDao().findMapBySql(sql, new Object[]{loanId});
		if (datas == null || datas.size() < 1)
			return;
		Map<String,Object> map = datas.get(0);
		// 取得客户ID对应的token
		String custToken = CacheUtil.getRedisData("COUSTOMER_TOKEN_" + map.get("custId"));
		Map<String,Object> m = new HashMap<String,Object>();
	    m.put("usedCredit", map.get("usedCredit"));
	    CacheUtil.refreshCustRedis(custToken, m);

	    // 同时更新客户的最近一次贷款日期为本次货款的结束时间
	    sql = "update t_customer a,t_loan b set a.last_loan_date=b.end_time where a.id=b.customer_id and b.id=?";
	    this.getDao().executeUpdateSql(sql, new Object[]{loanId});
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public void genLoanLateFee() throws Exception{
		// 读取系统参数
		Map<String,Object> params = JsonUtil.fromJson(CacheUtil.getRedisData("SYSCONFIG_"+ConstantsUtil.COMPANYID));
		int fee1 = CommonUtil.getIntValue(params.get("lateFeeDay"));
		int fee2 = CommonUtil.getIntValue(params.get("lateFeeDay2"));
		int days = CommonUtil.getIntValue(params.get("days"));
		// 针对days以前日期的产生滞纳金记录
		StringBuffer sb = new StringBuffer("insert into t_loan_latefee(loan_id,type,create_time,creator_id,money,remark)");
		sb.append(" select a.id,1,now(),-1,floor((a.req_money+a.late_fee-a.off_latefee) * ?/10000),'系统产生'")
		  .append(" from t_loan a where a.company_id=? and a.loan_status in (3,6) and now()>a.end_time")
		  .append(" and datediff(now(),a.end_time)<? and (a.late_fee - a.off_latefee)<a.req_money*2");
		this.getDao().executeUpdateSql(sb.toString(), new Object[]{fee1,ConstantsUtil.COMPANYID, days});
		// 针对day及以后日期产生滞纳金
		sb = new StringBuffer("insert into t_loan_latefee(loan_id,type,create_time,creator_id,money,remark)");
		sb.append(" select a.id,1,now(),-1,floor((a.req_money+a.late_fee-a.off_latefee) * ?/10000),'系统产生'")
		  .append(" from t_loan a where a.company_id=? and a.loan_status in (3,6) and now()>a.end_time")
		  .append(" and datediff(now(),a.end_time)>=? and (a.late_fee - a.off_latefee)<a.req_money*2");
		this.getDao().executeUpdateSql(sb.toString(), new Object[]{fee2,ConstantsUtil.COMPANYID, days});
		// 更新主表的合计滞纳金
		sb = new StringBuffer("update t_loan set late_fee=(select sum(b.money) from t_loan_latefee b where b.loan_id=t_loan.id and b.type=1)")
				.append(" where company_id=? and loan_status in (3,6) and now()>end_time and (late_fee - off_latefee)<req_money*2");
		this.getDao().executeUpdateSql(sb.toString(), new Object[]{ConstantsUtil.COMPANYID});
	}

	@Transactional
	public void loanPayBack(TLoanRepayment repayment, String payNo) throws Exception{
		// 更新预支付订单状态
		String sql = "update t_loan_repayment set pay_time=now(),pay_no=?,pay_status=1,refresh_status=1 where id=?";
		this.getDao().executeUpdateSql(sql, new Object[]{payNo,repayment.getId()});
		// 更新客户原订单状态为已还款
		sql = "update t_loan set loan_status=5,payback_payid=?,payback_date=now(),executionstatus=1 where id=?";
		this.getDao().executeUpdateSql(sql, new Object[]{repayment.getId(),repayment.getLoanId()});

		// 判断是否为续借，如果是续借，需要产生一笔新的贷款记录
		if (repayment.getType() == 1){
			// 取得原订单信息
			sql = "select req_money, agent_id, sub_agent_id,client_id,customer_id,order_no from t_loan a where a.id=?";
			Map<String,Object> loan = readService.findOneMapBySql(sql, new Object[]{repayment.getLoanId()});
			if (loan == null || loan.keySet().size() < 1)
				return;
			// 取得客户对应代理商的佣金
			int[] moneys = this.getAgentMoney(loan);

			TLoan tloan =new TLoan();
        	tloan.setCompanyId(repayment.getCompanyId());
        	StringBuffer sb = new StringBuffer("");
    		String orderDate = DateUtil.getCurrDate("yyyyMMddHHmmssSSS");
    		sb.append(orderDate).append((""+loan.get("order_no")).substring(17, 28));
    		sb.append(new Random().nextInt(9000) + 1000);
    		tloan.setOrderNo(sb.toString());
        	tloan.setCustomerId(CommonUtil.getLongValue(loan.get("customer_id")));
        	tloan.setLoanType(2);
        	tloan.setReqDate(repayment.getCreateTime());
        	tloan.setReqMoney(CommonUtil.getIntValue(loan.get("req_money")));
        	tloan.setHandleFee(repayment.getPayMoney() - repayment.getLatefee());
        	tloan.setDownMoney(tloan.getReqMoney() - tloan.getHandleFee());
        	tloan.setLoanStatus(3);
        	tloan.setAuditTime(new Date());
        	tloan.setAuditorId(-1L);
        	tloan.setAuditRemark("续借");
        	tloan.setDownType(1);
        	tloan.setDownTime(new Date());
        	tloan.setDownRemark("续借");
        	tloan.setStartTime(DateUtil.getNowDate());
        	tloan.setEndTime(DateUtil.dateFormat(DateUtil.dateFormat(DateUtil.dateAdd(DateUtil.DATE_INTERVAL_DAY, new Date(), 6),DateUtil.DATE_FORMAT_YMD)+" 23:59:59",DateUtil.DATE_FORMAT_YMDHMS));
        	tloan.setAgentId(CommonUtil.getLongValue(loan.get("agent_id")));
        	tloan.setAgentMoney(moneys[0]);
        	tloan.setSubAgentId(CommonUtil.getLongValue(loan.get("sub_agent_id")));
        	tloan.setSubAgentMoney(moneys[1]);
        	tloan.setClientId(""+loan.get("client_id"));
        	tloan = this.getDao().saveOrUpdate(tloan);
        	if (moneys[0]!=0 || moneys[1] != 0){
        		companyAgentMoneyService.addAgentMoneyLog(tloan.getAgentId(), tloan.getSubAgentId(), moneys[0], moneys[1], tloan.getId(), tloan.getCustomerId(), 2);
        	}
		}else{
			// 刷新客户缓存
			refreshCachedUsedCredit(repayment.getLoanId());
		}
	}

	private int[] getAgentMoney(Map<String,Object> loan) throws Exception{
		int[] moneys = new int[2];
		if (CommonUtil.getLongValue(loan.get("agent_id"))<1)
			return moneys;

		moneys = companyAgentMoneyService.getAgentMoneySetting(CommonUtil.getLongValue(loan.get("agent_id")), CommonUtil.getLongValue(loan.get("sub_agent_id")), 2);
		// 计算佣金
		int reqMoney = CommonUtil.getIntValue(loan.get("req_money"));
		moneys[0] = (int)Math.ceil(((double)(reqMoney * moneys[0])/1000));
		moneys[1] = (int)Math.ceil(((double)(reqMoney * moneys[1])/1000));
		return moneys;
	}
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateTloan(int id, int status) throws Exception {
		String tloanSql = "update t_loan set executionstatus = ? where id = ? ";
		this.getDao().executeUpdateSql(tloanSql,new Object[]{id,status});
	}

	@Transactional
	public void loanHandDown(String userId, String creditImg,Long companyId,Long loanId) throws Exception{
		// 此时需要重置贷款的开始时间和结束时间
		Date endTime = DateUtil.dateFormat(DateUtil.dateFormat(DateUtil.dateAdd(DateUtil.DATE_INTERVAL_DAY, new Date(), 6),DateUtil.DATE_FORMAT_YMD)+" 23:59:59",DateUtil.DATE_FORMAT_YMDHMS);
		String sql = "update t_loan set loan_status=3,down_type=2,down_time=now(),downer_id=?,down_img=?,start_time=curdate(),end_time=? where company_id=? and loan_status=4 and id=?";
		this.getDao().executeUpdateSql(sql, new Object[]{userId,creditImg,endTime,companyId,loanId});

		sql = "select req_money, agent_id, sub_agent_id,customer_id from t_loan a where a.id=?";
		Map<String,Object> loan = readService.findOneMapBySql(sql, new Object[]{loanId});
		if (loan == null || loan.keySet().size() < 1)
			return;
		Long agentId = CommonUtil.getLongValue(loan.get("agent_id"));
		Long subAgentId = CommonUtil.getLongValue(loan.get("sub_agent_id"));
		Long customerId = CommonUtil.getLongValue(loan.get("customer_id"));
		// 取得客户对应代理商的佣金
		int[] moneys = this.getAgentMoney(loan);
		if (moneys[0]!=0 || moneys[1] != 0){
    		companyAgentMoneyService.addAgentMoneyLog(agentId, subAgentId, moneys[0], moneys[1], loanId, customerId, 2);
    	}
	}
	@Transactional
	public void automaticLoan(String resultPay, String oidPaybill, String orderNo,String downMsg) throws Exception {
				List<Object> list = new ArrayList<Object>();
				//根据订单号，查询tloan，取出 一级，二级代理商的信息，进行插入
				List<Map<String,Object>> loanList =  this.getDao().findMapBySql("select loan_status loanStatus, agent_id agentId,sub_agent_id subAgentId,req_money reqMoney,agent_money agentMoney,sub_agent_money subAgentMoney,id loanId,customer_id customerId from t_loan where order_no = ?",new Object[]{orderNo});
				Map<String,Object> loanMap = null;
				if(null!=loanList&&loanList.size()>0) loanMap = loanList.get(0);
				long agentId = CommonUtil.getLongValue(loanMap.get("agentId"));
				long subAgentId = CommonUtil.getLongValue(loanMap.get("subAgentId"));
				int loanStatus = CommonUtil.getIntValue(loanMap.get("loanStatus"));
				int reqMoney = CommonUtil.getIntValue(loanMap.get("reqMoney"));
//				int[] companyAgentMoney = companyAgentMoneyService.getAgentMoneySetting(agentId, subAgentId, 2);

		if(loanStatus >=3){
			logger.warn("orderNo为{}的放款订单放款已经处理,无需重复处理!",orderNo);
			return;
		}
		Map<String,Object> loanParams = new HashMap<>();
		loanParams.put("agent_id",agentId);
		loanParams.put("sub_agent_id",subAgentId);
		loanParams.put("req_money",reqMoney);
		int[] companyAgentMoney = getAgentMoney(loanParams);

		StringBuffer sb = new StringBuffer("update t_loan set down_type = 1 ,down_time = now()");
				int status = 0;
				if (resultPay.equals(PaymentStatusEnum.PAYMENT_SUCCESS.getValue())) {
					// TODO 商户更新订单为成功，处理自己的业务逻辑
					 sb.append(", start_time = ? ,end_time = ?");
					 Date start = DateUtil
					          .getDateByString(DateUtil.dateFormat(new Date(), DateUtil.DATE_FORMAT_YMD),
					              DateUtil.DATE_FORMAT_YMD);//开始时间
					      Date end = DateUtil.dateAdd(DateUtil.DATE_INTERVAL_DAY, start, 7);
					      end = DateUtil.dateAdd(DateUtil.DATE_INTERVAL_SECOND, end, -1);//放款6天后的时间
					      status =3 ;
					      list.add(start);
					      list.add(end);
				} else if (resultPay.equals(PaymentStatusEnum.PAYMENT_FAILURE.getValue())) {
					// TODO 商户更新订单为失败，处理自己的业务逻辑
					  status = 4;
				}

				sb.append(" ,loan_status = ? ,down_remark = ? , executionstatus= 0,agent_money = ?,sub_agent_money = ? where order_no= ? ");
				list.add(status);
				list.add(downMsg);
				list.add(companyAgentMoney[0]);
				list.add(companyAgentMoney[1]);
				list.add(orderNo);
				//这个是异步回调，所以没有loan_id，除非在访问第三放的时候，把参数值丢过去，第三方在异步返回
				this.getDao().executeUpdateSql(sb.toString(),list.toArray(new Object[]{list.size()}));
				//判断是否有代理商
				if(MapUtils.isNotEmpty(loanMap)&&agentId>0){
					companyAgentMoneyService.addAgentMoneyLog(agentId,subAgentId,companyAgentMoney[0],companyAgentMoney[1],
							                                  CommonUtil.getLongValue(loanMap.get("loanId")),
							                                  CommonUtil.getLongValue(loanMap.get("customerId")),2);
				}
				String sql = "update t_biz_flow set modify_time=now(),status=?,org_biz_no=?,org_code =? where biz_no = ? and deleted=0";
				bizFlowService.updateBizFlow(sql,new Object[]{1,oidPaybill,"LIANLIANFANGKUAN",orderNo});

	}
}